#include "stdafx.h"
#include "tabfile_edit.h"

#include "ifile.h"
#include "tabfile.h"
#include "LuaScript.h"
#include "string_func.h"

CTabFileEdit::CTabFileEdit()
{
	m_nRowCount = 0;
	m_nColCount = 0;
	m_szFileName[0] = 0;
}

CTabFileEdit::~CTabFileEdit()
{
	UnInit();
}

bool CTabFileEdit::Init(const char* pcszFileName)
{
	int nRetCode = 0;
	CTabFile TabFile;

	strncpy(m_szFileName, pcszFileName, MAX_FILE_NAME_LEN);

	nRetCode = TabFile.Init(pcszFileName);
	LOG_PROCESS_ERROR(nRetCode);

	m_nRowCount = TabFile.GetRowCount();
	m_nColCount = TabFile.GetColCount();

	m_Data.resize(m_nRowCount);

	for (int nRowIndex = 0; nRowIndex < m_nRowCount; nRowIndex++)
	{
		m_Data[nRowIndex].resize(m_nColCount);

		for (int nColIndex = 0; nColIndex < m_nColCount; nColIndex++)
		{
			const char* pcszValue = TabFile.GetData(nColIndex, nRowIndex);
			LOG_PROCESS_ERROR(pcszValue);

			m_Data[nRowIndex][nColIndex] = pcszValue;
		}
	}

	for (int nColIndex = 0; nColIndex < m_nColCount; nColIndex++)
	{
		std::pair<MAP_NAME_2_COL::iterator, bool> InsRet;

		const char* pcszValue = GetData(nColIndex, 0);
		LOG_PROCESS_ERROR(pcszValue);

		InsRet = m_mapName2Col.insert(std::make_pair(pcszValue, nColIndex));
		LOG_PROCESS_ERROR(InsRet.second);
	}

	nRetCode = _LoadConfig(pcszFileName);
	LOG_PROCESS_ERROR(nRetCode);

	TabFile.UnInit();

	return true;
Exit0:
	TabFile.UnInit();

	UnInit();

	return false;
}

bool CTabFileEdit::UnInit(void)
{
	m_szFileName[0] = 0;

	return true;
}

bool CTabFileEdit::Save(void)
{
	char* pData = NULL;
	int nSize = 0;
	wchar_t* pFileData = NULL;
	int nFileSize = 0;
	IFile* pFile = NULL;

	for (int nRowIndex = 0; nRowIndex < m_nRowCount; nRowIndex++)
	{
		for (int nColIndex = 0; nColIndex < m_nColCount; nColIndex++)
		{
			nSize += m_Data[nRowIndex][nColIndex].size() * 2 + 4;
		}
	}
	
	pData = new char[nSize];
	LOG_PROCESS_ERROR(pData);

	int nPointer = 0;
	for (int nRowIndex = 0; nRowIndex < m_nRowCount; nRowIndex++)
	{
		for (int nColIndex = 0; nColIndex < m_nColCount; nColIndex++)
		{
			pData[nPointer++] = '\"';

			std::string& rValue = m_Data[nRowIndex][nColIndex];
			int nValueIndex = 0;
			while (nValueIndex < rValue.size())
			{
				if (rValue[nValueIndex] == '\"')
					pData[nPointer++] = '\"';

				*(pData + nPointer) = rValue[nValueIndex];
				nValueIndex++;
				nPointer++;
			}

			pData[nPointer++] = '\"';
			pData[nPointer++] = '\t';
		}
		pData[nPointer - 1] = '\r';
		pData[nPointer++] = '\n';
	}

	pFileData = new wchar_t[nSize * 2 + 1];
	LOG_PROCESS_ERROR(pFileData);

	((unsigned short*)pFileData)[0] = UTF16_BOM;

	nFileSize = utf8_2_utf16(pData, nSize, pFileData + 1, nSize * 2) * 2 + 2;
	LOG_PROCESS_ERROR(nFileSize > 0);

	pFile = g_OpenFile(m_szFileName, "wb");
	LOG_PROCESS_ERROR(pFile);

	int nWriteSize = pFile->Write(pFileData, 1, nFileSize);
	LOG_PROCESS_ERROR(nWriteSize == nFileSize);

	g_CloseFile(pFile);
	pFile = NULL;

	SAFE_DELETE_ARRAY(pData);
	SAFE_DELETE_ARRAY(pFileData);

	return true;
Exit0:
	SAFE_DELETE_ARRAY(pData);
	SAFE_DELETE_ARRAY(pFileData);
	if (pFile)
		g_CloseFile(pFile);

	return false;
}


int CTabFileEdit::GetRowCount(void)
{
	return m_nRowCount;
}

int CTabFileEdit::GetColCount(void)
{
	return m_nColCount;
}

const char* CTabFileEdit::GetData(int nColIndex, int nRowIndex)
{
	LOG_PROCESS_ERROR(nColIndex >= 0 && nColIndex < m_nColCount);
	LOG_PROCESS_ERROR(nRowIndex >= 0 && nRowIndex < m_nRowCount);

	return m_Data[nRowIndex][nColIndex].c_str();
Exit0:
	return NULL;
}

const char* CTabFileEdit::GetData(const char* pcszColName, int nRowIndex)
{
	MAP_NAME_2_COL::iterator it;

	LOG_PROCESS_ERROR(nRowIndex >= 0 && nRowIndex < m_nRowCount);

	it = m_mapName2Col.find(pcszColName);
	LOG_PROCESS_ERROR(it != m_mapName2Col.end());

	return m_Data[nRowIndex][it->second].c_str();
Exit0:
	return NULL;
}

const COLUMN_CONFIG* CTabFileEdit::GetConfig(int nColIndex)
{
	LOG_PROCESS_ERROR(nColIndex >= 0 && nColIndex < m_nColCount);

	return &m_vecColumnConfig[nColIndex];
Exit0:

	return NULL;
}

bool CTabFileEdit::AppendRow(void)
{
	m_nRowCount++;
	m_Data.resize(m_nRowCount);
	m_Data[m_nRowCount - 1].resize(m_nColCount);

	for (int nColIndex = 0; nColIndex < m_nColCount; nColIndex++)
	{
		m_Data[m_nRowCount - 1][nColIndex] = m_vecColumnConfig[nColIndex].strDefault;
	}

	return true;
}

bool CTabFileEdit::DeleteRow(int nRowIndex)
{
	LOG_PROCESS_ERROR(nRowIndex < m_nRowCount);

	m_Data.erase(m_Data.begin() + nRowIndex);

	m_nRowCount--;

	return true;
Exit0:
	return false;
}

bool CTabFileEdit::SetData(int nColIndex, int nRowIndex, const char* pcszValue)
{
	LOG_PROCESS_ERROR(nColIndex >= 0 && nColIndex < m_nColCount);
	LOG_PROCESS_ERROR(nRowIndex >= 0 && nRowIndex < m_nRowCount);

	m_Data[nRowIndex][nColIndex] = pcszValue;

	return true;
Exit0:

	return false;
}

bool CTabFileEdit::SetData(const char* pcszColName, int nRowIndex, const char* pcszValue)
{
	MAP_NAME_2_COL::iterator it;

	LOG_PROCESS_ERROR(nRowIndex >= 0 && nRowIndex < m_nRowCount);

	it = m_mapName2Col.find(pcszColName);
	LOG_PROCESS_ERROR(it != m_mapName2Col.end());

	m_Data[nRowIndex][it->second] = pcszValue;

	return true;
Exit0:

	return false;
}

bool CTabFileEdit::_LoadConfig(const char* pcszFileName)
{
	bool bResult = false;
	int nRetCode = 0;
	char		szFileName[MAX_FILE_NAME_LEN];
	CLuaScript	LuaScript;

	m_vecColumnConfig.resize(m_nColCount);
	for (int nColIndex = 0; nColIndex < m_nColCount; nColIndex++)
	{
		m_vecColumnConfig[nColIndex].nColumnType = ctString;
		m_vecColumnConfig[nColIndex].nMin = 0;
		m_vecColumnConfig[nColIndex].nMax = INT_MAX;

		const char* pcszValue = GetData(nColIndex, 0);
		LOG_PROCESS_ERROR(pcszValue);

		m_vecColumnConfig[nColIndex].strName = pcszValue;
	}

	strncpy(szFileName, pcszFileName, MAX_FILE_NAME_LEN);
	char* pLastDot = strrchr(szFileName, '.');
	if (pLastDot)
		*pLastDot = 0;
	strncat(szFileName, ".lua", MAX_FILE_NAME_LEN);

	nRetCode = LuaScript.Init();
	LOG_PROCESS_ERROR(nRetCode);

	LuaScript.AddGlobalInteger("ctNumber", ctNumber);
	LuaScript.AddGlobalInteger("ctEnum", ctEnum);
	LuaScript.AddGlobalInteger("ctString", ctString);
	LuaScript.AddGlobalInteger("ctRichText", ctRichText);
	LuaScript.AddGlobalInteger("ctID", ctID);

	nRetCode = LuaScript.LoadFromFile(szFileName);
	PROCESS_SUCCESS(nRetCode == NULL);

	lua_State* L = LuaScript.GetLuaState();
	LOG_PROCESS_ERROR(L);

	lua_getglobal(L, "TabConfig");
	int nColumn = lua_gettop(L);
	PROCESS_SUCCESS(!lua_istable(L, nColumn));

	lua_pushnil(L);
	while (lua_next(L, nColumn))
	{
		MAP_NAME_2_COL::iterator it;

		const char* pcszColumnName = lua_tostring(L, -2);
		LOG_PROCESS_ERROR(pcszColumnName);

		it = m_mapName2Col.find(pcszColumnName);
		LOG_PROCESS_ERROR(it != m_mapName2Col.end());
		int nColIndex = it->second;

		int nConfig = lua_gettop(L);
		LOG_PROCESS_ERROR(lua_istable(L, nConfig));

		lua_getfield(L, nConfig, "Type");
		if (lua_isnumber(L, -1))
			m_vecColumnConfig[nColIndex].nColumnType = lua_tonumber(L, -1);

		lua_getfield(L, nConfig, "Default");
		if (lua_isstring(L, -1))
			m_vecColumnConfig[nColIndex].strDefault = lua_tostring(L, -1);

		lua_getfield(L, nConfig, "Group");
		if (lua_isstring(L, -1))
			m_vecColumnConfig[nColIndex].strGroup = lua_tostring(L, -1);

		switch (m_vecColumnConfig[nColIndex].nColumnType)
		{
		case ctNumber:

			lua_getfield(L, nConfig, "Min");
			if (lua_isnumber(L, -1))
				m_vecColumnConfig[nColIndex].nMin = lua_tonumber(L, -1);

			lua_getfield(L, nConfig, "Max");
			if (lua_isnumber(L, -1))
				m_vecColumnConfig[nColIndex].nMax = lua_tonumber(L, -1);

			break;
		case ctEnum:
			{
				lua_getfield(L, nConfig, "Enum");

				int nEnumTable = lua_gettop(L);
				if (lua_istable(L, nEnumTable))
				{
					lua_pushnil(L);
					while (lua_next(L, nEnumTable))
					{
						LOG_PROCESS_ERROR(lua_isstring(L, -2));
						const char* pcszKey = lua_tostring(L, -2);

						LOG_PROCESS_ERROR(lua_isnumber(L, -1));
						int nNumber = lua_tonumber(L, -1);

						m_vecColumnConfig[nColIndex].mapID2String[nNumber] = pcszKey;
						m_vecColumnConfig[nColIndex].mapString2ID[pcszKey] = nNumber;

						lua_pop(L, 1);
					}
				}
			}
			break;
		case ctString:
			break;
		case ctRichText:
			break;
		case ctID:
			lua_getfield(L, nConfig, "LinkTable");
			if (lua_isstring(L, -1))
				m_vecColumnConfig[nColIndex].strLinkTable = lua_tostring(L, -1);

			lua_getfield(L, nConfig, "LinkColumn");
			if (lua_isstring(L, -1))
				m_vecColumnConfig[nColIndex].strLinkColumn = lua_tostring(L, -1);

			lua_getfield(L, nConfig, "DisplayColumn");
			if (lua_isstring(L, -1))
				m_vecColumnConfig[nColIndex].strDisplayColumn = lua_tostring(L, -1);

			break;
		default:
			LOG_PROCESS_ERROR(FALSE);
		}

		lua_settop(L, nConfig - 1);
	}

Exit1:
	bResult = true;
Exit0:
	nRetCode = LuaScript.UnInit();
	LOG_CHECK_ERROR(nRetCode);

	return bResult;
}
